﻿ 
namespace Common.Algorithm.Core;

public class FireflyService
{
    private static Random random = new Random();
    private static VectorBuilder<double> vectorBuilder = Vector<double>.Build;

    public static double gamma { get; set; } = 1.0;
    public static double alpha { get; set; } = 0.02;
    public int populationNum { get; set; } = 50;

    public static List<Firefly> fireflys { get; set; } = new List<Firefly>();

    private static List<double[]> ranges { get; set; } = new List<double[]>();
    private static Func<double[], double> targetFunc { get; set; }

    public static List<double[]> records { get; set; } = new List<double[]>();




    public FireflyService(Func<double[], double> _targetFunc, List<double[]> _ranges)
    {
        targetFunc = _targetFunc;
        ranges = _ranges;

        fireflys.Clear();
        for (int i = 0; i < populationNum; i++)
        {
            fireflys.Add(new Firefly(ranges));
        }

    }


    public void RunAlgm(int numberOfGeneration)
    {
        double unit = alpha / numberOfGeneration;

        //fireflys.ForEach(f => f.GetIntensity());
        //fireflys = fireflys.OrderBy(f => f.intensity).ToList();

        while (numberOfGeneration > 0)
        {
            numberOfGeneration--;
            alpha -= unit;

            fireflys.ForEach(f => f.GetLocalIntensity());
            fireflys = fireflys.OrderBy(f => f.intensity).ToList();

            for (int i = 0; i < fireflys.Count; i++)
            {
                var curFirefly = fireflys[i];
                //为最佳个体
                if (curFirefly == fireflys.Last())
                {
                    curFirefly.RandMove();
                    curFirefly.KeepInRange();
                    continue;
                }

                for (int j = 0; j < fireflys.Count; j++)
                {
                    if (i == j) continue;

                    var otherIntensity = fireflys[j].IntensityFrom(curFirefly.position);
                    if (curFirefly.intensity >= otherIntensity) { continue; }

                    curFirefly.attraction = fireflys[j].AttractionFrom(curFirefly.position);
                    curFirefly.Move(fireflys[j].position);
                    curFirefly.KeepInRange();
                }

            }

            records.AddRange(fireflys.Select(f => new double[] { f.position[0], f.position[1] }));
        }
    }

    public class Firefly
    {
        public Firefly(IEnumerable<double[]> _ranges)
        {
            var ranges = _ranges.ToList();
            List<double> paras = new List<double>();
            for (int i = 0; i < ranges.Count(); i++)
            {
                var rand = random.NextDouble();
                var para = ranges[i].Min() + (ranges[i].Max() - ranges[i].Min()) * rand;
                paras.Add(para);
                //vectorBuilder.Dense(dimension);
            }
            position = vectorBuilder.DenseOfArray(paras.ToArray());
        }

        #region props

        public Guid guid { get; set; } = Guid.NewGuid();
        public string name { get; set; } = Guid.NewGuid().ToString();
        /// <summary>
        /// 光强度
        /// </summary>
        public double intensity { get; set; }

        /// <summary>
        /// 引力
        /// </summary>
        public double attraction { get; set; } = 1.0;

        /// <summary>
        /// 所在位置
        /// </summary>
        public Vector<double> position { get; set; }

        #endregion


        #region method

        public double GetLocalIntensity()
        {
            this.intensity = targetFunc(position.ToArray());
            return this.intensity;
        }

        /// <summary>
        ///  来源处的光强度
        /// </summary>
        /// <param name="_pos"></param>
        /// <returns></returns>
        public double IntensityFrom(Vector<double> _pos)
        {
            var dis = (position - _pos).L2Norm();
            double newIntensity = this.intensity * Math.Exp(-1 * gamma * dis);
            return newIntensity;
        }

        /// <summary>
        /// 来源处的吸引力
        /// </summary>
        /// <param name="_pos"></param>
        /// <returns></returns>
        public double AttractionFrom(Vector<double> _pos)
        {
            var dis = (position - _pos).L2Norm();
            var newAttraction = this.attraction * Math.Exp(-1 * gamma * dis * dis);
            newAttraction = newAttraction < 0.1 ? 0.1 : newAttraction;  //永远保证最低引力 否则很容易发散
            this.attraction = newAttraction;
            return newAttraction;
        }

        /// <summary>
        /// 改变自己的位置
        /// </summary>
        /// <param name="_pos"></param>
        /// <returns></returns>
        public Vector<double> Move(Vector<double> _pos)
        {
            var vec = (_pos - this.position) / (_pos - this.position).L2Norm();
            this.position += this.attraction * vec + alpha * (random.NextDouble() - 0.5);
            return this.position;
        }

        /// <summary>
        /// 全方向随机震动
        /// </summary>
        public void RandMove()
        {
            List<double> doubles = new List<double>();
            for (int i = 0; i < position.Count; i++)
            {
                doubles.Add(alpha * (random.NextDouble() - 0.5));
            }
            var vec = vectorBuilder.DenseOfArray(doubles.ToArray());
            this.position = this.position + vec;
        }

        /// <summary>
        /// 
        /// </summary>
        public void KeepInRange()
        {
            for (int i = 0; i < position.Count; i++)
            {
                var range = ranges[i];

                this.position[i] = this.position[i] > range[1] ? range[1] : this.position[i];
                this.position[i] = this.position[i] < range[0] ? range[0] : this.position[i];
            }
        }

        #endregion
    }

}
